using System;
using System.Collections.Generic;
using System.Html.HtmlElements;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.Security;

namespace System.Html {
    /// <summary>
    /// Runs down the node structure and returns the html.
    /// </summary>
    public class HtmlFormatter {

        const string START_TAG_OPEN = "<";
        const string END_TAG_OPEN = "</";

        const string END_TAG_CLOSE = "/>";
        const string START_TAG_CLOSE = ">";

        const string ATTRIBUTE_EQUAL = "=\"";
        const string SINGLE_QUOTE = "\"";

        const string SINGLE_EMPTY_STRING = " ";

        public HtmlFormatter() { }

        /// <summary>
        /// Formats the HtmlDocument into html
        /// </summary>
        /// <param name="rootNode">The root node of the current recursion</param>
        public void Format(XmlNode rootNode, StringBuilder htmlStringBuilder) {

            if (rootNode.NodeType == XmlNodeType.Element) {
                ///append the string data
                htmlStringBuilder.Append(START_TAG_OPEN + rootNode.Name);
                if (rootNode.Attributes.Count > 0) {
                    FormatAttributes(rootNode, htmlStringBuilder);
                }

                Tag _nodeTag = HtmlParser.GetTagInstance(rootNode.Name);
                if (_nodeTag.AllowedChildren.Count > 0) {
                    htmlStringBuilder.Append(START_TAG_CLOSE);
                    foreach (XmlNode _node in rootNode.ChildNodes) {
                        Format(_node, htmlStringBuilder);
                    }
                    htmlStringBuilder.Append(END_TAG_OPEN + rootNode.Name + START_TAG_CLOSE);
                }
                else {
                    htmlStringBuilder.Append(END_TAG_CLOSE);
                }
            }
            else if (rootNode.NodeType == XmlNodeType.CDATA) {
                ///this is where the CDATA is "converted"
                htmlStringBuilder.Append(UnescapeXmlCDATA(rootNode.InnerText));
            }
        }
        /// <summary>
        /// Unescapes the string data for the cdata sections
        /// Orignally obtained from: http://www.csharper.net/blog/escape_xml_string_characters_in_c_.aspx
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string UnescapeXmlCDATA(string s) {
            string unxml = s;
            if (!string.IsNullOrEmpty(unxml)) {
                // replace entities with literal values
                unxml = unxml.Replace("&apos;", "'");
                unxml = unxml.Replace("&quot;", "\"");
                unxml = unxml.Replace("&amp;", "&");
                unxml = unxml.Replace("&lt;", "<");
                unxml = unxml.Replace("&gt;", ">");
            }
            return unxml;
        }

        /// <summary>
        /// Converts html special chars into those actual chars
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string ConvertHTMLSpecials(string data) {
            string _unescapedSpecials = data;
            if (!String.IsNullOrEmpty(_unescapedSpecials)) {
                _unescapedSpecials = _unescapedSpecials.Replace("&nbsp;", " ");
                _unescapedSpecials = _unescapedSpecials.Replace("&quot;", "\"");
            }
            return _unescapedSpecials;
        }

        private void FormatAttributes(XmlNode node, StringBuilder htmlStringBuilder) {
            foreach (XmlAttribute _attribute in node.Attributes) {
                htmlStringBuilder.Append(SINGLE_EMPTY_STRING);
                htmlStringBuilder.Append(_attribute.Name);
                htmlStringBuilder.Append(ATTRIBUTE_EQUAL);
                htmlStringBuilder.Append(_attribute.Value);
                htmlStringBuilder.Append(SINGLE_QUOTE);
            }
        }
    }
}
